package JasPerl::Util::Locale;

use 5.010;
use strict;
use warnings;

use parent qw(Exporter);

use feature qw(state);

use List::Util qw(first);
use Scalar::Util qw(blessed);
use I18N::LangTags qw(locale2language_tag);

use JasPerl::Util::Exceptions;

our @EXPORT_OK = qw(ROOT POSIX);

my $DEFAULT_LOCALE = eval {
    my $env = first { $ENV{$_} } qw(LANGUAGE LC_ALL LANG)
        or die "getting locale from environment failed";
    JasPerl::Util::Locale->parse($ENV{$env});
} || JasPerl::Util::Locale->POSIX;

sub ROOT {
    # a blessed "constant"
    state $const = JasPerl::Util::Locale->new('', '', '');
    return $const;
}

sub POSIX {
    # a blessed "constant"
    state $const = JasPerl::Util::Locale->new('en', 'US', 'POSIX');
    return $const;
}

sub new {
    my ($class, $language, $country, $variant) = @_;
    JasPerl::Util::NullPointerException->throw()
        unless defined $language;
    $country //= '';
    $variant //= '';

    bless [ lc $language, uc $country, $variant ], $class;
}

sub parse {
    my ($class, $s) = @_;
    JasPerl::Util::NullPointerException->throw()
        unless defined $s;

    # strip all locale extensions, e.g. "en_US.UTF-8" -> en_US
    # TODO: handle scripts, language tags, etc.
    $s =~ s/[^a-zA-Z_-].*$//;

    return $class->ROOT unless length $s;
    return $class->POSIX if $s eq 'C' or $s eq 'POSIX';
    return $class->JasPerl::Util::Locale::new(split /[_-]/, $s, 3);
}

sub from {
    my ($class, $from) = @_;
    # $class->parse handles undef
    return $class->parse($from) unless ref $from;

    if (blessed $from) {
        return $from if $from->isa($class);
    }

    JasPerl::Util::CoercionException->throw(
        "Cannot create $class from " . (ref $from || 'scalar')
    );
}

sub from_language_tag {
    # FIXME
    return $_[0]->parse($_[1]);
}

sub get_default {
    return $DEFAULT_LOCALE;
}

sub set_default {
    my ($class, $locale) = @_;
    JasPerl::Util::NullPointerException->throw()
        unless defined $locale;
    $DEFAULT_LOCALE = $locale;
}

sub get_language {
    return $_[0]->[0];
}

sub get_country {
    return $_[0]->[1];
}

sub get_variant {
    return $_[0]->[2];
}

sub as_language_tag {
    return locale2language_tag($_[0]->as_string);
}

sub as_string {
    my $self = shift;

    # variant must accompany a well-formed language or country code
    return '' unless $self->get_language() or $self->get_country();

    my $s = $self->get_language();
    $s .= '_' . $self->get_country()
        if $self->get_country() or $self->get_variant();
    $s .= '_' . $self->get_variant()
        if $self->get_variant();
    return $s;
}

use overload (
    '""' => 'as_string'
);

1;
